﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Xml.Serialization;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
#if BIZTALKSERVICES
using System.ServiceBus;
#endif

namespace dasBlog.Storage
{
    [DataContract(Namespace = Names.DataContractNamespace)]
    public sealed class PathSegmentName : IEquatable<PathSegmentName>
    {
        [DataContract(Namespace = Names.DataContractNamespace)]
        public enum Roles
        {
            [EnumMember]
            Posts,
            [EnumMember]
            Comments,
            [EnumMember]
            Pictures,
            [EnumMember]
            Media,
            [EnumMember]
            Annotations,
            [EnumMember]
            Persons,
            [EnumMember]
            Tags,
            [EnumMember]
            Trackings,
            [EnumMember]
            Ratings,
            [EnumMember]
            Events,
            [EnumMember]
            Custom
        }

        [DataMember]
        public Roles Role { get; set; }
        [DataMember]
        public string Value { get; set; }

        public PathSegmentName()
        {

        }

        public PathSegmentName(Roles nodeType)
        {
            if (nodeType == Roles.Custom)
                throw new InvalidOperationException("Can't define custom node types with this constructor");

            Role = nodeType;
            Value = RoleToString(nodeType);
        }

        public PathSegmentName(string name)
        {
            Role = RoleFromString(name);
            Value = name;
        }

        public override bool Equals(object obj)
        {
            PathSegmentName that = (PathSegmentName)obj;
            return Role == that.Role && Value == that.Value;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode() ^ Value.GetHashCode();
        }

        public override string ToString()
        {
            return Value;
        }

        public static string RoleToString(Roles nodeType)
        {
            switch (nodeType)
            {
                case Roles.Annotations:
                    return Properties.Resources.StorageNodeTypeAnnotations;
                case Roles.Comments:
                    return Properties.Resources.StorageNodeTypeComments;
                case Roles.Media:
                    return Properties.Resources.StorageNodeTypeMedia;
                case Roles.Persons:
                    return Properties.Resources.StorageNodeTypePersons;
                case Roles.Pictures:
                    return Properties.Resources.StorageNodeTypePictures;
                case Roles.Posts:
                    return Properties.Resources.StorageNodeTypePosts;
                case Roles.Tags:
                    return Properties.Resources.StorageNodeTypeTags;
                case Roles.Trackings:
                    return Properties.Resources.StorageNodeTypeTrackings;
                case Roles.Ratings:
                    return Properties.Resources.StorageNodeTypeRatings;
                case Roles.Events:
                    return Properties.Resources.StorageNodeTypeEvents;
                default:
                    return null;
            }
        }

        public static Roles RoleFromString(string name)
        {
            if (name == Properties.Resources.StorageNodeTypeAnnotations)
                return (Roles.Annotations);
            else if (name == Properties.Resources.StorageNodeTypeComments)
                return (Roles.Comments);
            else if (name == Properties.Resources.StorageNodeTypeMedia)
                return (Roles.Media);
            else if (name == Properties.Resources.StorageNodeTypePersons)
                return (Roles.Persons);
            else if (name == Properties.Resources.StorageNodeTypePictures)
                return (Roles.Pictures);
            else if (name == Properties.Resources.StorageNodeTypePosts)
                return (Roles.Posts);
            else if (name == Properties.Resources.StorageNodeTypeTags)
                return (Roles.Tags);
            else if (name == Properties.Resources.StorageNodeTypeTrackings)
                return (Roles.Trackings);
            else if (name == Properties.Resources.StorageNodeTypeRatings)
                return (Roles.Ratings);
            else if (name == Properties.Resources.StorageNodeTypeEvents)
                return (Roles.Events);
            else
                return Roles.Custom;
        }

        public static readonly PathSegmentName Posts = new PathSegmentName(Roles.Posts);
        public static readonly PathSegmentName Comments = new PathSegmentName(Roles.Comments);
        public static readonly PathSegmentName Pictures = new PathSegmentName(Roles.Pictures);
        public static readonly PathSegmentName Media = new PathSegmentName(Roles.Media);
        public static readonly PathSegmentName Annotations = new PathSegmentName(Roles.Annotations);
        public static readonly PathSegmentName Persons = new PathSegmentName(Roles.Persons);
        public static readonly PathSegmentName Tags = new PathSegmentName(Roles.Tags);
        public static readonly PathSegmentName Trackings = new PathSegmentName(Roles.Trackings);
        public static readonly PathSegmentName Ratings = new PathSegmentName(Roles.Ratings);
        public static readonly PathSegmentName Events = new PathSegmentName(Roles.Events);


        #region IEquatable<NodeName> Members

        public bool Equals(PathSegmentName other)
        {
            return Role == other.Role && Value == other.Value;
        }

        #endregion

        public static bool operator ==(PathSegmentName a, PathSegmentName b)
        {
            return a.Equals(b);
        }

        public static bool operator !=(PathSegmentName a, PathSegmentName b)
        {
            return !a.Equals(b);
        }
    }



    [DataContract(Namespace = Names.DataContractNamespace)]
    public sealed class NodeDescription
    {
        [DataMember]
        public PathMappings Paths { get; set; }
        [DataMember]
        public EndpointReferenceType EndpointReference { get; set; }
        
        private IStorageNode _storageNode;
        private bool _isBound=false;

        public NodeDescription()
        {
            Paths = new PathMappings();
        }

        public NodeDescription(IStorageNode node)
            : this()
        {
            _storageNode = node;
        }

        public NodeDescription(PathMappings childMappings)
            : this()
        {
            this.Paths = childMappings;
        }


        public NodeDescription(PathMappings childMappings, IStorageNode node)
            : this(childMappings)
        {
            _storageNode = node;
        }

        internal IStorageNode GetStorageNode()
        {
            return _storageNode;
        }
        
        internal void SetStorageNode(IStorageNode node)
        {
            _storageNode = node;
        }

        internal void BindNode()
        {
            if (_isBound) return;
            _isBound = true;

            if (_storageNode == null && this.EndpointReference != null)
            {
                var endpointRef = this.EndpointReference;
                ContractDescription mexDescription = EndpointContractDescription<IMetadataExchange>.Description;
                ContractDescription stgDescription = EndpointContractDescription<IStorageNode>.Description;
                ContractDescription stmDescription = EndpointContractDescription<IStreamStorageNode>.Description;
                var endpointAddress = endpointRef.ToEndpointAddress();

                if (endpointRef.PortType.Value.Name == mexDescription.Name &&
                    endpointRef.PortType.Value.Namespace == mexDescription.Namespace)
                {
                    MetadataExchangeClient client = new MetadataExchangeClient(GetBindingFromEndpoint(endpointAddress.Uri));
                    WsdlImporter importer = new WsdlImporter(client.GetMetadata(endpointAddress));
                    var eps = importer.ImportAllEndpoints();
                    foreach (ServiceEndpoint endpoint in
                             eps.Where((s) =>
                                 s.Contract.Name == Names.IStorageNodeName &&
                                 s.Contract.Namespace == Names.IStorageNodeNamespace))
                    {
                        ChannelFactory<IStorageNodeChannel> factory = new ChannelFactory<IStorageNodeChannel>(endpoint.Binding, endpoint.Address);
                        endpoint.Behaviors.Add(new SimpleAuthenticationBehavior());

                        var channel = factory.CreateChannel();
                        SetStorageNode(channel);
                    }
                }
                else if (endpointRef.PortType.Value.Name == stgDescription.Name &&
                         endpointRef.PortType.Value.Namespace == stgDescription.Namespace)
                {
                    ChannelFactory<IStorageNodeChannel> factory = new ChannelFactory<IStorageNodeChannel>(GetBindingFromEndpoint(endpointAddress.Uri), endpointAddress);
                    factory.Endpoint.Behaviors.Add(new SimpleAuthenticationBehavior());

                    var channel = factory.CreateChannel();
                    SetStorageNode(channel);
                }
                else if (endpointRef.PortType.Value.Name == stmDescription.Name &&
                         endpointRef.PortType.Value.Namespace == stmDescription.Namespace)
                {
                    ChannelFactory<IStreamStorageNodeChannel> factory = new ChannelFactory<IStreamStorageNodeChannel>(GetBindingFromEndpoint(endpointAddress.Uri), endpointRef.ToEndpointAddress());
                    factory.Endpoint.Behaviors.Add(new SimpleAuthenticationBehavior());

                    var channel = factory.CreateChannel();
                    SetStorageNode(channel);
                }
            }

            foreach (PathMapping mapping in this.Paths)
            {
                mapping.Node.BindNode();
            }
        }

        private static Binding GetBindingFromEndpoint(Uri endpointAddress)
        {
            Binding binding;
            switch (endpointAddress.Scheme)
            {
#if BIZTALKSERVICES
                case "sb":
                    binding = new RelayBinding(RelayConnectionMode.HybridDuplexSession);
                    break;
#endif
                default:
                case "http":
                case "https":
                    binding = new BasicHttpBinding();
                    break;
            }
            return binding;
        }
        
    }



}
